# -*- coding: utf8 -*-
import sys, time, socket, threading, os, random, BaseHTTPServer,zlib

########### PASAJE DE PARAMETROS #############

# Parametros pasados en la corrida -> client.py <ip_propia> <port_listen> <nick> <ip supernodo> <port_supernodo>
ip = sys.argv[1]
tcp_port = int(sys.argv[2])
nick = sys.argv[3]
supernode_ip = sys.argv[4]
supernode_port = int(sys.argv[5])


########### FUNCIONES Y CLASES #############

############# SERVIDOR HTTP ################

class MiThread(threading.Thread):  
	def __init__(self):  
		threading.Thread.__init__(self)  
		self.f = ""
	def run(self):
		server_class = BaseHTTPServer.HTTPServer
		httpd = server_class((ip, (tcp_port+1)), MyHandler)
		
		inicio = "Inicio del Servidor: "
		ap = time.asctime()
		inicio=inicio + ap
		inicio = inicio + " # Server: " + str (ip) + " - Puerto: " + str(tcp_port+1)
		print inicio
		#f = open ("salida.txt","w")
		#f.write (inicio)
		
		#f.close()
		
		#f = open ("salida.txt","a")
		
		try:
			httpd.serve_forever()
		except KeyboardInterrupt:
			print "\nInterrumpido por el Usuario"
			#f.close()
			#f = open ("salida.txt","a")
			#f.write ("\nInterrumpido por el Usuario")
			#f.close()
			
		httpd.server_close()
		
		inicio = "\nDetengo el Servidor: "
		ap = time.asctime()
		inicio=inicio + ap
		
		print inicio
		#f = open ("salida.txt","a")
		#f.write (inicio)
		#f.close()
	 

class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
	def do_HEAD(s):
		
		s.send_response(200)
		s.send_header("Content-type", "text/html")
		print "hola"
		s.end_headers()
	
	def do_GET(s):
			
		if s.path=='/':
			s.path='/index.html'
		
		archivo = os.path.basename(s.path)
		
		try:
			html = open (archivo).read()
			s.wfile.write(str(html))
		except:
			s.wfile.write("<html><title>Error 404</title>")
			s.wfile.write("<body><p>Error 4404</p>")
			s.wfile.write("</body></html>")

## FIn de inicializacion 

## PROGRAMA EN SI 

# Thread que maneja una conexion TCP, de la cual recibe mensajes y realiza acciones a partir de estos

class TCP_Connection(threading.Thread):

    def __init__(self, peer, name, imagenes):
        threading.Thread.__init__(self)
        self.peer = peer
        self.name_peer = name 
        self.flag = 1
        self.imagenes = imagenes
		
    def send(self,data):
        # print 'Le digo a %s: %s' % (self.name_peer, data)
        self.peer.send(data)

    def close(self):
        self.flag = 0
	
    def run(self):
        while self.flag:
            data = self.peer.recv(1024)                       
            if data == 'interest':
                # Aca hará las accions tendientes a enviarle el interes al usuario
                self.send(self.interest)
            else:   
                print 'Me dice %s: %s' %(self.name_peer, data)
        self.peer.close()

#node = Node((ip, tcp_port), [], nick, imagenes, '')
class Node():
    # La clase del nodo
    def __init__(self, address, list_users, nick, imagenes, query):
        self.address = address
        self.tcp = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.tcp.bind(self.address)
        self.tcp.listen(10)
        self.tcp.settimeout(10)
        self.lock = threading.Lock()
        self.imagenes = imagenes
        self.query = query
        # ETAPA DE INICIALIZACIÓN
        self.flag = 1
        # NODOS HOJA CONECTADOS
        self.users = list_users
        self.nick = nick
        
        d = {}
        self.connections = d
    
    def add_user(self, name, address):
        # INSTANCIA UN USUARIO Y LO AGREGA A LA LISTA
        self.users[name] = address
        return name

    def add_conx(self, name, socket):
        # INSTANCIA UNA CONX Y LA AGREGA A LA LISTA
        self.connections[name] = TCP_Connection(socket, name, self.interest)
        self.connections[name].start()
        return ">>Se conecto al chat contigo " + name

    def disconnect(self):
        # DESCONECTA AL NODO DE LOS DEMAS SUPERNODOS, PONE EL FLAG EN CERO Y 'APAGA' EL NODO (POR EL ACCEPT QUE ESTA EN ESCUCHA)
        self.flag = 0
        for conx in self.connections:
            conx.close()
        return "Good bye!"
				
# Clase que implementa terminal
class Terminal(threading.Thread):
    # TERMINAL DE CONTROL DEL NODO
    def __init__(self, node):
        threading.Thread.__init__(self)
        self.node = node

    def run(self):
        while self.node.flag:
            cmd = raw_input('>>')
            
            if (cmd=='exit'):
                # SETEA EL FLAG EN 0, LO QUE INTERRUMPE TODOS LOS LOOPS DE ESCUCHA
                self.node.flag = 0 
            if(cmd=='list users'):
                # MUESTRO USUARIOS
                print 'Usuarios\n'
                self.node.lock.acquire()
                for user, tupla in self.node.users.iteritems():
                    print user, tupla
                self.node.lock.release()
            elif(cmd=='list conx'):
                # MUESTRO CONEXIONES
                print 'Conexiones\n'
                self.node.lock.acquire()
                for user, tupla in self.node.connections.iteritems():
                    print user
                self.node.lock.release()
            elif(cmd=='disconnect'):
                # ME DESCONECTO DE LAS CONEXIONES EXISTENTES
                print 'Desconectando...'
                print self.node.disconnect()
                self.node.tcp.close()
                sys.exit()
            elif(cmd.split(' ')[0] == 'search_picture'):
                command, self.node.query = cmd.split(' ')
                # Le paso el query al supernodo
                # Genero mi Identificador unico
                codigo = random.randrange(1, 9999999)
                # Defino el TTL del mensaje
                ttl = 3
                socket_supernode.send('202|' + str(my_id) + '|' + str(codigo) + '|' + self.node.query + '|' + str(ttl) + '||')
                # Aqui el supernodo le devuelve las fotos al nodo
                relevant_pictures = socket_supernode.recv(1024)
                print "Llegaron: ", relevant_pictures
                relevant_pictures = relevant_pictures.split('||')[0]
                relevant_pictures = relevant_pictures[0:-1]
                relevant_pictures = relevant_pictures[4:]
                relevant_pictures = relevant_pictures.split('@')
                
                print " Tabla de resultados "
                print 
                print " Indice | id_Nodo | Nombre de la Imagen "
                indice = 0
                print "relevant_pictures", relevant_pictures
                for p in relevant_pictures:
                    try:
                        id_nodo, name_picture = p.split('/')
                        print indice, id_nodo, name_picture
                        indice+=1
                        #node.add_user(nick_peer, list_users[user])
                    except Exception, e:
                        print 'ERROR:', e, 'Ojo que no pudo leer la relevant con valor: ', p
            
                else:
                    print 'Usuario inexistente o Comando incorrecto\n'                   


            elif (len(cmd.split(':')) == 2): # Si es un mensaje dirigido a alguien
                user, comment = cmd.split(':')
                if user in self.node.users.keys():
                    try:
                        self.node.connections[user].send(comment)                                			    
                    except: # Si la conexion no está establecida, la establezco yo como usuario
                        socket_efimero = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                        socket_efimero.connect(self.node.users[user])
                        socket_efimero.send(self.node.nick)
                        self.node.add_conx(user, socket_efimero)
                        self.node.connections[user].send(comment)
                else:
                    print 'Usuario inexistente o Comando incorrecto\n'                   
            else:
                print 'Comando incorrecto\n'


########### Comienza la conexion con el supernodo #############

# Conexion con el supernodo
t = MiThread()
t.start()

socket_supernode = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_supernode.connect((supernode_ip,supernode_port))

# Envia el nick al supernodo
socket_supernode.send('200|' + nick + '|' + ip + ':' + str(tcp_port) + '||')

# Responde con el id
# Me va a llegar un 201 como respuesta del SN
response_id = socket_supernode.recv(1024)
print response_id # Es decir 201|Id_Nodo
my_id = response_id.split('|')[1]


########### Comienza la interfaz con el usuario #############

def chars2bytes (string):
    return map(ord, string)

def bytes2chars (bytes):
    return "".join(map(chr, bytes))

def funcionhash (string):
	
	imag = ""   
	for im in imagenes:
		imag+=im

	mex = chars2bytes(imag)
	md5 = 0
	for i in mex:
		md5+=i
	
	return md5
	
print '--NODO--'
print 'Red Peer to Peer - Taller Libre II - Universidad Nacional de Lujan 2012-2013'
print 'Petrocelli David MArcelo | L/N: 108779'
print 'Bienvenido -> ' + nick + '!!!'
# print 'El id: ' + my_id

# El nodo le envia el interes al supernodo
'''

import zlib

encoder = zlib.compressobj()
data = encoder.compress("Cuando pueda le contestoo |lamentablemente estamos cruzados con la disponibilidad|Ahora estoy tapada y no tengo tiempo para dedicarle| (lo tenia en enero) por otro lado Marcelo me comenta algunas cosas que no se como tomarlas |Cuando pueda responderle te copio")
data = data + encoder.flush()
print data

new_data = repr(zlib.decompress(data))
print new_data

'''
carpeta = raw_input('Ingrese Carpeta de Archivos -: ')
imagenes = os.listdir(carpeta)

md5 = funcionhash(imagenes)
		
print md5
# Envio el MD5
socket_supernode.send('220|' + ip + ':' + str(tcp_port) + '|' + str(nick) + '|'+ str(md5) + '||')
res_221 = socket_supernode.recv(128)
#print res_221

# Decodifico lo que me dijo el Servidor
res_221 = res_221.split('||')[0]
res_221 = res_221.split('|')[1]
print res_221

if (res_221 == 'no'):
	pic = ""
	for im in imagenes:
		pic += im+"|"
		#socket_supernode.send('210|' + my_id + '|' + im + '||')
		#res_211 = socket_supernode.recv(1024)
		#print res_211
	
	# COMPRIMO LOS DATOS PARA QUE VIAJEN EN LA RED
	
	encoder = zlib.compressobj()
	pic = encoder.compress(pic)
	pic = pic + encoder.flush()
	
	socket_supernode.send('210|'+ ip + ':' + str(tcp_port) + '|' + str(nick) + '|' + str(md5) +'|' + pic + '||')
	
	# ME RESPONDE CON UN 211
	res_211 = socket_supernode.recv(1024)
	print res_211
else:
	print "los datos que dispone del SN son iguales a los del LeafNode"
	

print 'Comandos posibles:'
print 'Buscar foto -> search_picture'
print 'Listar usuarios -> list users'
print 'Listar conexiones -> list conx'
print 'Desconectarse -> disconnect'
print

node = Node((ip, tcp_port), [], nick, imagenes, '')
# ABRE LA CONSOLA
Terminal(node).start()
while node.flag:
    try:
        peer, address = node.tcp.accept()
        nick_peer = peer.recv(1024)
        while nick_peer in node.users.keys():
            nick_peer = nick_peer + 'I' # Le agrega un palito I hasta que no exista el usuario   
        node.add_user(nick_peer, address)
        print node.add_conx(nick_peer, peer)
    except Exception,e:
	    pass

# Se le envia el mensaje de desconexion al supernodo y se desconecta
socket_supernode.send('204|'+my_id+'||')
response_desc = socket_supernode.recv(1024)
print response_desc
socket_supernode.close()

